home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / Other Langs / Tickle-4.0 (tcl) / src / tar_list.c < prev    next >
Text File  |  1993-11-06  |  10KB  |  499 lines

  1.  
  2. #pragma segment TAR
  3.  
  4. /*
  5.  * Macintosh Tar
  6.  *
  7.  * Modified by Craig Ruff for use on the Macintosh.
  8.  */
  9. /*
  10.  * List a tar archive.
  11.  *
  12.  * Also includes support routines for reading a tar archive.
  13.  *
  14.  * Pubic Domain version written 26 Aug 1985 by John Gilmore (ihnp4!hoptoad!gnu).
  15.  *
  16.  * @(#)list.c 1.18 9/23/86 Public Domain - gnu
  17.  */
  18.  
  19. #include "tar.h"
  20. #include "stat.h"
  21. #include <string.h>
  22.  
  23.  
  24. char *ctime();                /* From libc.a */
  25.  
  26. #define    isodigit(c)    ( ((c) >= '0') && ((c) <= '7') )
  27. #define isspace(c)    ((c) == ' ')
  28.  
  29. long FromOct();            /* Decode octal number */
  30.  
  31. union record *head;        /* Points to current archive header */
  32. struct
  33.     {
  34.     long    st_size;
  35.     long    st_mtime;
  36.     } hstat;            /* Fake stat struct for compat. */
  37.  
  38. void    PrintHeader();
  39. void    ReadAnd();
  40. Boolean    ListArchive(), SkipFile();
  41.  
  42. /*
  43.  * List - list an archive file
  44.  */
  45.  
  46. #ifdef TCLAPPL
  47.  
  48. List()
  49.     {
  50.     Point        where;
  51.     SFReply        reply;
  52.     Boolean        oldAutoPage = autoPage;
  53.     extern WindowPtr    theFeedbackWindow;
  54.  
  55.     /*
  56.      * Use standard file to get the archive file.
  57.      * Always do a screen at a time if to the screen.
  58.      */
  59.     where.h = where.v = 75;
  60.     MyGetFile(where, "\pName of TAR file:", nil, -1, nil, nil, &reply);
  61.     if (!reply.good)
  62.         return;
  63.  
  64.     arName = reply.fName;
  65.     WDDirVRef(reply.vRefNum, &arVRefNum, &arDirID);
  66.  
  67.     if (WindInit())
  68.         return;
  69.  
  70. /*TGE*/    UBegYield();
  71.  
  72. /*TGE*/    ShowFeedback();
  73.     
  74.     autoPage = true;
  75. /*TGE*/    SetPort(theFeedbackWindow);
  76.     TextFace(underline);
  77.     WPrintf(header);
  78. /*TGE*/    SetPort(theFeedbackWindow);
  79.     TextFace(0);
  80.  
  81.     ReadAnd(ListArchive);
  82.     CloseArchive();
  83.     WPrintf("--- tar listing completed.");
  84.  
  85.     WindEnd(true);
  86.     autoPage = oldAutoPage;
  87. /*TGE*/    UEndYield();
  88. }
  89.  
  90. #endif /* TCLAPPL */
  91.  
  92. Cmd_ListArchive(clientData, interp, argc, argv)
  93.     char        *clientData;
  94.     Tcl_Interp    *interp;
  95.     int            argc;
  96.     char        *argv[];
  97.     {
  98.     Boolean        oldAutoPage = autoPage;
  99.     char        name[256], *ptr;
  100.     struct stat    statbuf;
  101. #pragma unused (clientData)
  102.     
  103.     if (argc != 3)
  104.         {
  105.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  106.                         " short|long archive_filename\"", (char *) NULL);
  107.         return TCL_ERROR;
  108.         }
  109.  
  110.     if ( stat( argv[2], &statbuf ) < 0 )
  111.         {
  112.         Tcl_AppendResult(interp, "could not locate \"", argv[2],
  113.                         "\" - ", Tcl_PosixError(interp), NULL);
  114.         return TCL_ERROR;
  115.         }
  116.     
  117.     if ( S_ISDIR(statbuf.st_mode) )
  118.         {
  119.         Tcl_AppendResult(interp, "\"", argv[2], "\" is a directory", NULL);
  120.         return TCL_ERROR;
  121.         }
  122.     
  123.     arVRefNum = statbuf.st_dev;
  124.     arDirID = statbuf.st_parid;
  125.  
  126.     /*
  127.      * Extract and print the files as found in the archive.
  128.      */
  129.     if (argv[1][0] == 'l' || argv[1][0] == 'L')
  130.         tar_list_format = 1;
  131.     else
  132.         tar_list_format = 0;
  133.     
  134.     ptr = strrchr( argv[2], ':' );
  135.     if (ptr == NULL)
  136.         strcpy(name, argv[2]);
  137.     else
  138.         strcpy(name, ptr + 1);
  139.     c2pstr(name);
  140.     arName = name;
  141.  
  142.     UBegYield();
  143.  
  144.     autoPage = true;
  145.  
  146.     if (tar_list_format)
  147.         {
  148.         Tcl_AppendResult( interp, header, (char *)0 );
  149.         Tcl_AppendResult( interp, "\015", (char *)0 );
  150.         }
  151.     
  152.     tar_scripting = 1;
  153.     tar_listing = 1;
  154.     tar_interp = interp;
  155.  
  156.     ReadAnd(ListArchive);
  157.     CloseArchive();
  158.  
  159.     tar_scripting = 0;
  160.     tar_listing = 0;
  161.     tar_interp = NULL;
  162.  
  163.     UEndYield();
  164.     
  165.     autoPage = oldAutoPage;
  166.  
  167.     return TCL_OK;
  168.     }
  169.  
  170. /*
  171.  * Main loop for reading an archive.
  172.  */
  173. void
  174. ReadAnd(doSomething)
  175. Boolean (*doSomething)();
  176. {
  177.     int         status = 1;
  178.     int         prevStatus;
  179.     Boolean        errFound = false;
  180.     CursHandle    cursor;
  181.     extern int cancel_current_op;
  182.     extern short pause_op;
  183.  
  184.     if ((cursor = GetCursor(watchCursor)) != nil)
  185.         SetCursor(*cursor);
  186.  
  187.     OpenArchive(1);        /* Open for reading */
  188.  
  189.     while (!errFound) {
  190. /*TGE*/    DoYield();
  191.         if (pause_op)
  192.             while (pause_op)
  193.                 pausing();
  194.         
  195.         if (cancel_current_op)
  196.             break;
  197. /*TGE*/    
  198. #ifdef NEVER_DEFINED
  199.     EventRecord    e;
  200.         SystemTask();
  201.         if (EventAvail(keyDownMask, &e) && (e.what != nullEvent)) {
  202.             if (
  203.                 (e.modifiers & cmdKey) &&
  204.                 ((e.message & charCodeMask) == '.')
  205.             ) {
  206.                 GetNextEvent(keyDownMask, &e);
  207.                 break;
  208.             }
  209.         }
  210. #endif
  211.  
  212.         prevStatus = status;
  213.         status = ReadHeader();
  214.         switch (status) {
  215.         case 1:            /* Valid header */
  216.             /* We should decode next field (mode) first... */
  217.             /* Ensure incoming names are null terminated. */
  218.             head->header.name[NAMSIZ-1] = '\0';
  219.             errFound = (*doSomething)();
  220.             continue;
  221.  
  222.             /*
  223.              * If the previous header was good, tell them
  224.              * that we are skipping bad ones.
  225.              */
  226.         case 0:            /* Invalid header */
  227.         case0:
  228.             UseRec(head);
  229.             if (prevStatus == 1) {
  230.                 PgmAlert("\pReadAnd",
  231.                     "\pSkipping to next file header...",
  232.                     nil);
  233.             }
  234.             continue;
  235.  
  236.         case 2:            /* Block of zeroes */
  237.             if (ignorez)    
  238.                 goto case0;    /* Just skip if asked */
  239.             /* FALL THRU */
  240.         case (int) EOF:        /* End of archive */
  241.             break;
  242.         }
  243.         break;
  244.     }
  245.  
  246.     CloseArchive();
  247.     SetCursor(&qd.arrow);
  248. }        
  249.  
  250.  
  251. /*
  252.  * Print a header record, based on tar options.
  253.  */
  254. Boolean
  255. ListArchive()
  256. {
  257.     long t;
  258.  
  259.     /* Save the record */
  260.     SaveRec(&head);
  261.  
  262.     /*
  263.      * Print the header record.
  264.      * Don't sling the names too fast!
  265.      */
  266.     PrintHeader();
  267.     if (!autoPage)
  268.         Delay(60L, &t);
  269.  
  270.     /* Skip past it in the archive */
  271.     SaveRec((union record **) 0);    /* Unsave it */
  272.     UseRec(head);
  273.  
  274.     /* Skip to the next header on the archive */
  275.     return(SkipFile((long)hstat.st_size));
  276. }
  277.  
  278.  
  279. /*
  280.  * Read a record that's supposed to be a header record.
  281.  * Return its address in "head", and if it is good, the file's
  282.  * size in hstat.st_size.
  283.  *
  284.  * Return 1 for success, 0 if the checksum is bad, EOF on eof,
  285.  * 2 for a block full of zeros (EOF marker).
  286.  *
  287.  * You must always userec(head) to skip past the header which this
  288.  * routine reads.
  289.  */
  290. int
  291. ReadHeader()
  292. {
  293.     register int    i;
  294.     register long    sum, recsum;
  295.     register char    *p;
  296.     register union record *header;
  297.  
  298.     header = FindRec();
  299.     head = header;        /* This is our current header */
  300.     if (header == nil)
  301.         return(EOF);
  302.  
  303.     recsum = FromOct(8,  header->header.chksum);
  304.     sum = 0;
  305.     p = header->charptr;
  306.     for (i = sizeof(*header); --i >= 0;) {
  307.         /*
  308.          * We can't use unsigned char here because of old compilers,
  309.          * e.g. V7.
  310.          */
  311.         sum += 0xFF & *p++;
  312.     }
  313.  
  314.     /* Adjust checksum to count the "chksum" field as blanks. */
  315.     for (i = sizeof(header->header.chksum); --i >= 0;)
  316.         sum -= 0xFF & header->header.chksum[i];
  317.     sum += ' ' * sizeof(header->header.chksum);
  318.  
  319.     if (sum == recsum) {
  320.         /*
  321.          * Good record.  Decode file size and return.
  322.          */
  323.         hstat.st_size = FromOct(1+12, header->header.size);
  324.         return(1);
  325.     }
  326.  
  327.     if (sum == 8 * ' ') {
  328.         /*
  329.          * This is a zeroed block...whole block is 0's except
  330.          * for the 8 blanks we faked for the checksum field.
  331.          */
  332.         return(2);
  333.     }
  334.  
  335.     return(0);
  336. }
  337.  
  338. /* 
  339.  * Decode things from a file header record into a "struct stat".
  340.  *
  341.  * read_header() has already decoded the checksum and length, so we don't.
  342.  *
  343.  * If wantug != 0, we want the uid/group info decoded from Unix Standard
  344.  * tapes (for extraction).  If == 0, we are just printing anyway, so save time.
  345.  */
  346. DecodeHeader(header, st, wantug)
  347. register union record    *header;
  348. register struct stat    *st;
  349. int    wantug;
  350. {
  351. #pragma unused(wantug)
  352.  
  353.     st->st_mtime = FromOct(1+12, header->header.mtime);
  354. }
  355.  
  356. /*
  357.  * Quick and dirty octal conversion.
  358.  *
  359.  * Result is -1 if the field is invalid (all blank, or nonoctal).
  360.  */
  361. long
  362. FromOct(digs, where)
  363. register int    digs;
  364. register char    *where;
  365. {
  366.     register long    value;
  367.  
  368.     while (isspace(*where)) {        /* Skip spaces */
  369.         where++;
  370.         if (--digs <= 0)
  371.             return(-1);        /* All blank field */
  372.     }
  373.  
  374.     value = 0;
  375.     while (digs > 0 && isodigit(*where)) {    /* Scan til nonoctal */
  376.         value = (value << 3) | (*where++ - '0');
  377.         --digs;
  378.     }
  379.  
  380.     if (digs > 0 && *where && !isspace(*where))
  381.         return(-1);            /* Ended on non-space/nul */
  382.  
  383.     return(value);
  384. }
  385.  
  386. /*
  387.  * Actually print it.
  388.  */
  389. #define    UGSWIDTH    9    /* min width of User, group, size */
  390. #define    DATEWIDTH    19    /* Last mod date */
  391. static int ugswidth = UGSWIDTH;    /* Max width encountered so far */
  392.  
  393. void
  394. PrintHeader()
  395. {
  396.     char mode;
  397.     char *timestamp;
  398.     char size[12];        /* Holds a formatted long */
  399.     long longie;        /* To make ctime() call portable */
  400.     int    pad;
  401.  
  402.     DecodeHeader(head, &hstat, 0);
  403.     /* File type and mode */
  404.     mode = '?';
  405.     switch (head->header.linkflag) {
  406.     case LF_NORMAL:
  407.     case LF_OLDNORMAL:
  408.         mode = 'F'; 
  409.         if ('/' == head->header.name[strlen(head->header.name)-1])
  410.             mode = 'D';
  411.         break;
  412.  
  413.     case LF_DIR:
  414.         mode = 'D';
  415.         break;
  416.     }
  417.  
  418.     /* 
  419.      * Convert to Mac based time from Unix based time.
  420.      */
  421.     longie = hstat.st_mtime + TIMEDIFF;
  422.  
  423.     timestamp = ctime(&longie);
  424.     timestamp[16] = '\0';
  425.     timestamp[24] = '\0';
  426.  
  427.     /* Format the file size or major/minor device numbers */
  428.     switch (head->header.linkflag) {
  429.     default:
  430.         (void) sprintf(size, "?????");
  431.         break;
  432.  
  433.     case LF_DIR:
  434.         (void) sprintf(size, "%.*s", UGSWIDTH, "");
  435.         break;
  436.  
  437.     case LF_OLDNORMAL:
  438.     case LF_NORMAL:
  439.         (void) sprintf(size, "%ld", hstat.st_size);
  440.         break;
  441.     }
  442.  
  443.     /* Figure out padding and print the whole line. */
  444.     pad = strlen(size) + 1;
  445.     if (pad > ugswidth)
  446.         ugswidth = pad;
  447.  
  448.     if (tar_scripting && tar_listing)
  449.         {
  450.         char    buffer[512];
  451.         
  452.         if (tar_list_format)
  453.             {
  454.             sprintf(buffer, "%c %*s%s %s %s %.*s",
  455.                     mode, ugswidth - pad, "", size,
  456.                     timestamp+4, timestamp+20, sizeof(head->header.name),
  457.                     head->header.name);
  458.             Tcl_AppendResult( tar_interp, buffer, "\015", (char *)0 );
  459.             }
  460.         else
  461.             {
  462.             sprintf(buffer, "%.*s", sizeof(head->header.name), head->header.name);
  463.             Tcl_AppendElement( tar_interp, buffer );
  464.             }
  465.         }
  466.     else
  467.         {
  468.         WPrintf("%c %*s%s %s %s %.*s",
  469.                     mode, ugswidth - pad, "", size,
  470.                     timestamp+4, timestamp+20, sizeof(head->header.name),
  471.                     head->header.name);
  472.         }
  473.     }
  474.  
  475. /*
  476.  * Skip over <size> bytes of data in records in the archive.
  477.  */
  478. Boolean
  479. SkipFile(size)
  480. register long size;
  481. {
  482.     union record *x;
  483.  
  484.     while (size > 0) {
  485.         x = FindRec();
  486.         if (x == nil) {    /* Check it... */
  487.             PgmAlert("\pSkipFile",
  488.                 "\pUnexpected EOF on archive file",
  489.                 nil);
  490.             return(true);
  491.         }
  492.         
  493.         UseRec(x);
  494.         size -= RECORDSIZE;
  495.     }
  496.     
  497.     return(false);
  498. }
  499.